package lxs.swift.operation.match;

import lxs.swift.verify.Verifies;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

public class Item<T, R, C extends Case<T>, TD extends Todo<T, R>> {
    public final C tCase;

    public final TD tTodo;

    private Item(C tCase, TD tTodo) {
        this.tCase = tCase;
        this.tTodo = tTodo;
    }

    public static <T, R, C extends Case<T>, TD extends Todo<T, R>> Item<T, R, C, TD> of(@NotNull Predicate<T> is, @NotNull Function<T, R> function) {
        Objects.requireNonNull(is);
        Objects.requireNonNull(function);
        Case<T> test = is::test;
        Todo<T, R> todo = function::apply;
        return new Item<>(((C) test), ((TD) todo));
    }

    public static <T, C extends Case<T>, TD extends Todo<T, Void>> Item<T, Void, C, TD> runOf(@NotNull Predicate<T> is, @NotNull Consumer<T> consumer) {
        Objects.requireNonNull(is);
        Objects.requireNonNull(consumer);
        Case<T> test = is::test;
        Todo<T, Void> todo = v -> {
            consumer.accept(v);
            return null;
        };
        return new Item<>(((C) test), ((TD) todo));
    }

    public static <T, R, TD extends Todo<T, R>> Item<T, R, Case<T>, TD> $if(@NotNull Predicate<T> is, @NotNull Function<T, R> function) {
        return of(is, function);
    }

    public static <T, TD extends Todo<T, Void>> Item<T, Void, Case<T>, TD> $if(@NotNull Predicate<T> is, @NotNull Consumer<T> consumer) {
        return runOf(is, consumer);
    }

    public static <T, R, TD extends Todo<T, R>> Item<T, R, Case<T>, TD> $if(@NotNull Predicate<T> is, R r) {
        return of(is, v -> r);
    }

    public static <TR extends T, T, R, TD extends Todo<T, R>> Item<T, R, Case<T>, TD> $type(@NotNull Class<TR> trClass, R value) {
        Objects.requireNonNull(trClass);
        return of(trClass::isInstance, v -> value);
    }

    public static <TR extends T, T, R, TD extends Todo<T, R>> Item<T, R, Case<T>, TD> $type(@NotNull Class<TR> trClass, @NotNull Function<TR, R> function) {
        Objects.requireNonNull(trClass);
        Objects.requireNonNull(function);
        return of(trClass::isInstance, v -> {
            TR tr = (TR) v;
            return function.apply(tr);
        });
    }

    public static <TR extends T, T, TD extends Todo<T, Void>> Item<T, Void, Case<T>, TD> $type(@NotNull Class<TR> trClass, @NotNull Consumer<T> consumer) {
        Objects.requireNonNull(trClass);
        Objects.requireNonNull(consumer);
        return runOf(trClass::isInstance, v -> {
            TR tr = (TR) v;
            consumer.accept(tr);
        });
    }

    public static <T, R, TD extends Todo<T, R>> Item<T, R, Case<T>, TD> $eq(T eq, R value) {
        return of(v -> Verifies.isObjectEq(v, eq), v -> value);
    }

    public static <T, R, TD extends Todo<T, R>> Item<T, R, Case<T>, TD> $eq(T eq, @NotNull Function<T, R> function) {
        return of(v -> Verifies.isObjectEq(v, eq), function);
    }

    public static <T, R, C extends Case<T>, TD extends Todo<T, R>> Item<T, R, C, TD> $(@NotNull Function<T, R> function) {
        return of(v -> true, function);
    }

    public static <T, R, C extends Case<T>, TD extends Todo<T, R>> Item<T, R, C, TD> $(R other) {
        return of(v -> true, v -> other);
    }
}
